package de.koller.worlders.render;

import de.koller.oceanFramework.OceanException;
import de.koller.oceanFramework.gl.OceanGame;
import de.koller.oceanFramework.math.Vec;
import de.koller.oceanFramework.tree.Child;
import de.koller.worlders.WSea;
import de.koller.worlders.gameplay.IGameplayPlayerObject;
import de.koller.worlders.gameplay.gui.PausedScreen;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JOptionPane;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;

public class RenderRootGL extends WSea {
	
	@Child public final ViewportGL viewportGL = new ViewportGL();
	@Child public final WRendering worlderV = new WRendering();
	
	private IGLRender tilemap = null;
	@Child private final PausedScreen pauseScreen = new PausedScreen();
	private final Set<IGLRender> worldElements = new HashSet<>();
	private final Set<IGLRender> transparentWorldElement = new HashSet<>();
	private final Set<IGLRender> guiElements = new HashSet<>();
	
	private float gZoom = 1;
	private float gRota = 0;
	private boolean fullscreen = false;

	@Override
	protected void init() {
		super.init();
		
		Display.setVSyncEnabled( false );
		
		GL11.glEnable( GL11.GL_CULL_FACE );
		GL11.glCullFace( GL11.GL_FRONT );

		GL11.glEnable( GL11.GL_DEPTH_TEST );
		GL11.glDepthFunc( GL11.GL_LEQUAL );
		
		GL11.glAlphaFunc( GL11.GL_GREATER, 0.1f );
		GL11.glEnable( GL11.GL_ALPHA_TEST );
	}
	
	@Override
	protected void update( int elapsedTime ) {
		super.update( elapsedTime );
		
		if( wroot.getInput().isKeyDown( Keyboard.KEY_U ) )
			changeGZoom( 0.01f );
		if( wroot.getInput().isKeyDown( Keyboard.KEY_J ) )
			changeGZoom( -0.01f );
		
//		if( wroot.getInput().isKeyDown( Keyboard.KEY_Z ) )
//			changeGRota( 1f );
//		if( wroot.getInput().isKeyDown( Keyboard.KEY_H ) )
//			changeGRota( -1f );
		
		IGameplayPlayerObject gpo = wroot.gamePlayer.getGPO();
		if( gpo != null )
			gRota = gpo.getScreenAngle();
		if( wroot.getDebug().isActive() )
			gRota = 0;
		
		if( wroot.getInput().isKeyPressed( Keyboard.KEY_F11 ) ) {
			fullscreen = !fullscreen;
			try {
				Display.setFullscreen( fullscreen );
				Display.setVSyncEnabled( fullscreen );
			} catch( LWJGLException ex ) {
				System.err.println( "Can't start fullscreen: " + ex.toString() );
			}
		}
	}
	
	public void renderGame( int elapsedTime ) {
		//pre
		viewportGL.bind();
		
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
				
		GL11.glEnable( GL11.GL_STENCIL_TEST );
		GL11.glStencilFunc( GL11.GL_ALWAYS, 1, 1 );
		GL11.glStencilOp( GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE );
		
		tilemap.renderGL( elapsedTime );
		
		worlderV.setTransform();
		
		for( IGLRender r : worldElements )
			r.renderGL( elapsedTime );
		
		worlderV.unsetTransform();
		
		renderTranparentWorldElements( elapsedTime);
		
		//post
		wroot.getTileMap().renderAfterAll();
		
		GL11.glClear( GL11.GL_STENCIL_BUFFER_BIT );
		GL11.glDisable( GL11.GL_STENCIL_TEST );
	
		for( IGLRender r : guiElements )
			r.renderGL( elapsedTime );
		
		worlderV.setTransform();
		renderCrosshair();
		worlderV.unsetTransform();
		
		renderPauseScreen();
		
		viewportGL.unbind();
		
		viewportGL.renderFrame();
	
		OceanGame.checkForErrorGL( "GLError" );
	}

	private void renderPauseScreen() {
		if( pauseScreen == null || !wroot.isPaused() )
			return;
		
		GL11.glEnable( GL11.GL_BLEND );
		GL11.glBlendFunc( GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA );
		
		pauseScreen.renderGL( 0 );
		
		GL11.glDisable( GL11.GL_BLEND );
	}
	
	private void renderCrosshair() {
		if( !wroot.getEntityContainer().isShowCrosshair() )
			return;
		
		GL11.glEnable( GL11.GL_COLOR_LOGIC_OP );
		GL11.glLogicOp( GL11.GL_XOR );
		
		Vec m = wroot.getEntityContainer().getMouseWorldPosition();
		VisualMap.crosshair.setRotation( -gRota );
		VisualMap.crosshair.setRotationOffsetX( .5f );
		VisualMap.crosshair.setRotationOffsetY( .5f );
		VisualMap.crosshair.renderObject( m.getXf()-.5f, m.getYf()-.5f, 1, 1, 99, false );
		
		GL11.glDisable( GL11.GL_COLOR_LOGIC_OP );
	}
	
	private void renderTranparentWorldElements( int elapsedTime ) {
		GL11.glEnable( GL11.GL_BLEND );
		GL11.glBlendFunc( GL11.GL_CONSTANT_ALPHA, GL11.GL_ONE_MINUS_CONSTANT_ALPHA );
		worlderV.setTransform();
		for( IGLRender r : transparentWorldElement )
			r.renderGL( elapsedTime );
		worlderV.unsetTransform();
		GL11.glDisable( GL11.GL_BLEND );
	}
	
	public static void checkForErrorGL( String message ) {
		int e = GL11.glGetError();
		if( e != GL11.GL_NO_ERROR )
			throw new OceanException( "OpenGLError 0x" + Integer.toHexString( e ) + ":" + message );	
	}
	
	public void addGUIElement( IGLRender element ) {
		guiElements.add( element );
	}
	
	public void removeGUIElement( IGLRender element ) {
		guiElements.remove( element );
	}
	
	public void addWorldElement( IGLRender element ) {
		worldElements.add( element );
	}
	
	public void removeWorldlement( IGLRender element ) {
		worldElements.remove( element );
	}

	public void addTransparentWorldElement( IGLRender element ) {
		transparentWorldElement.add( element );
	}
	
	public void removeTransparentWorldlement( IGLRender element ) {
		transparentWorldElement.remove( element );
	}
	
	public void setTilemapV( IGLRender tilemap ) {
		if( this.tilemap != null )
			throw new OceanException( "An Tilemapview is already set: " + tilemap );
		this.tilemap = tilemap;
	}
	
	public int getGUIElementNumber() {
		return guiElements.size();
	}

	public int getWorldElementNumber() {
		return worldElements.size();
	}
	
	public int getTransparentWorldElementNumber() {
		return transparentWorldElement.size();
	}
	
	public void changeGZoom( float v ) {
		this.gZoom += v;
		if( this.gZoom < 1 ) this.gZoom = 1;
	}
	
//	public void changeGRota( float r ) {
//		this.gRota += r;
//		this.gRota += 360;
//		this.gRota %= 360;
//	}

	public float getgZoom() {
		return gZoom;
	}

	public float getgRota() {
		return gRota;
	}
	
	public void showWorldElements() {
		String sh = "";
		for( IGLRender r : this.worldElements )
			sh += r.toString() + "\n";
		
		JOptionPane.showMessageDialog( null, sh );
	}

	public void showGuiElements() {
		String sh = "";
		for( IGLRender r : this.guiElements )
			sh += r.toString() + "\n";
		
		JOptionPane.showMessageDialog( null, sh );
	}
	
}
